# =============================================================================
# SCRAP_X_AMLO_v3.R — v3.0
# Script de recolección de tweets de @lopezobrador_ en X/Twitter
# Proyecto: Corpus para investigación sobre erosión democrática en América Latina
# =============================================================================
#
# PERÍODOS:
#   P0: Pre-presidencia  2009-10-13 → 2018-11-30
#   P1: Presidencia      2018-12-01 → 2024-09-30
#   P2: Post-presidencia 2024-10-01 → hoy
#
# ARCHIVOS GENERADOS:
#   tweets_amlo_P0_prepresidencia.csv
#   tweets_amlo_P1_presidencia.csv
#   tweets_amlo_P2_postpresidencia.csv
#   tweets_amlo_COMPLETO.csv
#   dias_saturados_amlo.csv
# =============================================================================

setwd("TU_RUTA_AQUI")  # Ejemplo: "~/proyectos/AMLO"

library(reticulate)
library(readr)
library(dplyr)
library(lubridate)
library(stringr)

use_virtualenv("~/playwright-env", required = TRUE)
py_run_file("scraper.py")

# =============================================================================
# PARÁMETROS
# =============================================================================

AUTH_TOKEN <- "TU_AUTH_TOKEN_AQUI"

CONFIG <- list(
  modo_headless     = FALSE,
  timeout_pagina    = 60000,
  timeout_elemento  = 10000,
  scroll_pixels     = 1500,
  delay_min         = 2.0,
  delay_max         = 4.0
)

USUARIO                <- "lopezobrador_"
UMBRAL_SATURACION      <- 18
ARCHIVO_CONSOLIDADO    <- "tweets_amlo_COMPLETO.csv"
ARCHIVO_DIAS_SATURADOS <- "dias_saturados_amlo.csv"

PERIODOS <- list(
  P0 = list(
    nombre   = "P0_prepresidencia",
    etiqueta = "Pre-presidencia",
    inicio   = as.Date("2009-10-13"),
    fin      = as.Date("2018-11-30"),
    archivo  = "tweets_amlo_P0_prepresidencia.csv"
  ),
  P1 = list(
    nombre   = "P1_presidencia",
    etiqueta = "Presidencia",
    inicio   = as.Date("2018-12-01"),
    fin      = as.Date("2024-09-30"),
    archivo  = "tweets_amlo_P1_presidencia.csv"
  ),
  P2 = list(
    nombre   = "P2_postpresidencia",
    etiqueta = "Post-presidencia",
    inicio   = as.Date("2024-10-01"),
    fin      = Sys.Date(),
    archivo  = "tweets_amlo_P2_postpresidencia.csv"
  )
)

# =============================================================================
# AUXILIARES
# =============================================================================
`%||%` <- function(x, y) if (!is.null(x) && length(x) > 0) x else y

leer_csv_seguro <- function(archivo) {
  read_csv(archivo, show_col_types = FALSE,
           col_types = cols(
             id           = col_character(),
             fecha        = col_character(),
             texto        = col_character(),
             likes        = col_integer(),
             retweets     = col_integer(),
             replies      = col_integer(),
             url          = col_character(),
             es_retweet   = col_logical(),
             es_respuesta = col_logical(),
             periodo      = col_character(),
             .default     = col_character()
           ))
}

tweets_a_df <- function(tweets_raw, periodo_nombre = "") {
  if (length(tweets_raw) == 0) return(data.frame())
  bind_rows(lapply(tweets_raw, function(t) {
    data.frame(
      id           = as.character(t$id           %||% ""),
      fecha        = as.character(t$fecha        %||% ""),
      texto        = as.character(t$texto        %||% ""),
      likes        = as.integer( t$likes        %||% 0L),
      retweets     = as.integer( t$retweets     %||% 0L),
      replies      = as.integer( t$replies      %||% 0L),
      url          = as.character(t$url          %||% ""),
      es_retweet   = as.logical( t$es_retweet   %||% FALSE),
      es_respuesta = as.logical( t$es_respuesta %||% FALSE),
      periodo      = as.character(periodo_nombre),
      stringsAsFactors = FALSE
    )
  }))
}

url_busqueda_diaria <- function(usuario, desde, hasta) {
  hasta_mas1 <- format(as.Date(hasta) + 1, "%Y-%m-%d")
  paste0(
    "https://x.com/search?q=(from%3A", usuario, ")",
    "%20until%3A", hasta_mas1,
    "%20since%3A", desde,
    "&src=typed_query&f=live"
  )
}

url_busqueda_horaria <- function(usuario, fecha, hora_inicio_utc, hora_fin_utc) {
  since <- sprintf("%s_%02d:00:00_UTC", fecha, hora_inicio_utc)
  until <- sprintf("%s_%02d:00:00_UTC", fecha, hora_fin_utc)
  paste0(
    "https://x.com/search?q=(from%3A", usuario, ")",
    "%20until%3A", until,
    "%20since%3A", since,
    "&src=typed_query&f=live"
  )
}

scrape_url_periodo <- function(url, ids_vistos, periodo_nombre, verbose = TRUE) {
  tweets_raw <- tryCatch({
    py$scrape_url_sesion(url, ids_vistos)
  }, error = function(e) {
    if (verbose) cat(sprintf("    [ERROR] %s\n", conditionMessage(e)))
    list()
  })
  
  if (length(tweets_raw) == 0) return(data.frame())
  
  tweets_a_df(tweets_raw, periodo_nombre) %>%
    filter(!id %in% ids_vistos, !es_retweet)
}

# =============================================================================
# FUNCIÓN PRINCIPAL
# =============================================================================
procesar_periodo <- function(periodo) {
  
  periodo_nombre_str <- periodo$nombre
  periodo_etiqueta   <- periodo$etiqueta
  periodo_inicio     <- periodo$inicio
  periodo_fin        <- periodo$fin
  periodo_archivo    <- periodo$archivo
  
  cat(sprintf("\n%s\n", strrep("═", 60)))
  cat(sprintf("  PERÍODO: %s\n", periodo_etiqueta))
  cat(sprintf("  Rango  : %s → %s\n",
              format(periodo_inicio, "%d %b %Y"),
              format(periodo_fin,    "%d %b %Y")))
  cat(sprintf("%s\n\n", strrep("═", 60)))
  
  if (file.exists(periodo_archivo)) {
    df_acumulado <- leer_csv_seguro(periodo_archivo)
    cat(sprintf("✓ Progreso previo cargado: %d tweets\n", nrow(df_acumulado)))
  } else {
    df_acumulado <- data.frame()
    cat("✓ Iniciando período desde cero\n")
  }
  
  ids_vistos <- if (nrow(df_acumulado) > 0) as.character(df_acumulado$id) else character(0)
  
  if (file.exists(ARCHIVO_DIAS_SATURADOS)) {
    dias_sat_log <- read_csv(ARCHIVO_DIAS_SATURADOS, show_col_types = FALSE,
                             col_types = cols(
                               fecha      = col_character(),
                               periodo    = col_character(),
                               tweets_dia = col_integer()
                             ))
  } else {
    dias_sat_log <- data.frame(fecha = character(0), periodo = character(0),
                               tweets_dia = integer(0))
  }
  
  dias_saturados_nuevos <- character(0)
  
  # ── FASE 1: Barrido diario ─────────────────────────────────────────────────
  cat(sprintf("\n--- FASE 1: Barrido diario ---\n"))
  dias <- seq(periodo_inicio, periodo_fin, by = "day")
  cat(sprintf("Total días a procesar: %d\n\n", length(dias)))
  
  for (i in seq_along(dias)) {
    fecha_str <- format(dias[i], "%Y-%m-%d")
    
    if (i %% 100 == 0 || i == 1) {
      cat(sprintf("[%d/%d] %s | Acumulado: %d tweets\n",
                  i, length(dias), fecha_str, length(ids_vistos)))
    }
    
    url      <- url_busqueda_diaria(USUARIO, fecha_str, fecha_str)
    df_nuevo <- scrape_url_periodo(url, ids_vistos, periodo_nombre_str, verbose = FALSE)
    
    if (nrow(df_nuevo) > 0) {
      if (nrow(df_nuevo) >= UMBRAL_SATURACION) {
        dias_saturados_nuevos <- c(dias_saturados_nuevos, fecha_str)
        cat(sprintf("  ⚠️  DÍA SATURADO: %s (%d tweets)\n", fecha_str, nrow(df_nuevo)))
      }
      
      df_acumulado <- bind_rows(df_acumulado, df_nuevo) %>%
        distinct(id, .keep_all = TRUE)
      ids_vistos <- as.character(df_acumulado$id)
      write_csv(df_acumulado, periodo_archivo)
      
      cat(sprintf("  +%d | %s | Total: %d\n",
                  nrow(df_nuevo), fecha_str, nrow(df_acumulado)))
    }
    
    Sys.sleep(runif(1, 0.5, 1.0))
  }
  
  cat(sprintf("\n✓ Fase 1 completada. Total: %d tweets\n", nrow(df_acumulado)))
  
  if (length(dias_saturados_nuevos) > 0) {
    nuevos_sat <- data.frame(
      fecha      = dias_saturados_nuevos,
      periodo    = periodo_nombre_str,
      tweets_dia = NA_integer_
    )
    dias_sat_log <- bind_rows(dias_sat_log, nuevos_sat) %>%
      distinct(fecha, .keep_all = TRUE)
    write_csv(dias_sat_log, ARCHIVO_DIAS_SATURADOS)
  }
  
  # ── FASE 2: Re-búsqueda horaria ────────────────────────────────────────────
  dias_sat_periodo <- dias_sat_log %>%
    filter(periodo == periodo_nombre_str) %>%
    pull(fecha)
  
  if (length(dias_sat_periodo) > 0) {
    cat(sprintf("\n--- FASE 2: Re-búsqueda horaria (%d días saturados) ---\n",
                length(dias_sat_periodo)))
    
    franjas_utc <- list(
      list(inicio = 0,  fin = 6),
      list(inicio = 6,  fin = 12),
      list(inicio = 12, fin = 18),
      list(inicio = 18, fin = 24)
    )
    
    for (fecha_sat in dias_sat_periodo) {
      cat(sprintf("  %s:", fecha_sat))
      nuevos_dia <- 0
      
      for (franja in franjas_utc) {
        url      <- url_busqueda_horaria(USUARIO, fecha_sat, franja$inicio, franja$fin)
        df_nuevo <- scrape_url_periodo(url, ids_vistos, periodo_nombre_str, verbose = FALSE)
        
        if (nrow(df_nuevo) > 0) {
          if (nrow(df_nuevo) >= UMBRAL_SATURACION) {
            cat(sprintf("\n    ⚠️  FRANJA SATURADA: %02d:00-%02d:00 UTC\n",
                        franja$inicio, franja$fin))
          }
          df_acumulado <- bind_rows(df_acumulado, df_nuevo) %>%
            distinct(id, .keep_all = TRUE)
          ids_vistos <- as.character(df_acumulado$id)
          nuevos_dia <- nuevos_dia + nrow(df_nuevo)
        }
        
        Sys.sleep(runif(1, 1, 2))
      }
      
      if (nuevos_dia > 0) {
        write_csv(df_acumulado, periodo_archivo)
        cat(sprintf(" +%d recuperados | Total: %d\n", nuevos_dia, nrow(df_acumulado)))
      } else {
        cat(" sin adicionales\n")
      }
    }
  } else {
    cat("\n--- FASE 2: Sin días saturados ---\n")
  }
  
  # ── FASE 3: Verificación en períodos críticos ──────────────────────────────
  cat(sprintf("\n--- FASE 3: Verificación de días en blanco en períodos críticos ---\n"))
  
  periodos_criticos <- list(
    list(inicio = "2006-01-01", fin = "2006-07-02", desc = "Campaña presidencial 2006"),
    list(inicio = "2006-07-03", fin = "2006-12-31", desc = "Postelectoralismo 2006"),
    list(inicio = "2012-01-01", fin = "2012-07-01", desc = "Campaña presidencial 2012"),
    list(inicio = "2017-01-01", fin = "2017-12-31", desc = "Campaña presidencial 2018 inicio"),
    list(inicio = "2018-01-01", fin = "2018-07-01", desc = "Campaña presidencial 2018"),
    list(inicio = "2019-01-01", fin = "2019-12-31", desc = "Primer año de gobierno"),
    list(inicio = "2020-03-01", fin = "2020-06-30", desc = "COVID-19"),
    list(inicio = "2021-06-01", fin = "2021-06-30", desc = "Elecciones intermedias"),
    list(inicio = "2022-01-01", fin = "2022-04-30", desc = "Revocación de mandato"),
    list(inicio = "2024-06-01", fin = "2024-06-30", desc = "Elección presidencial 2024")
  )
  
  criticos_en_periodo <- Filter(function(p) {
    p$fin >= as.character(periodo_inicio) && p$inicio <= as.character(periodo_fin)
  }, periodos_criticos)
  
  if (length(criticos_en_periodo) == 0 || nrow(df_acumulado) == 0) {
    cat("  Sin períodos críticos en este rango.\n")
  } else {
    tweets_con_fecha <- df_acumulado %>%
      filter(!is.na(fecha), fecha != "") %>%
      mutate(fecha_solo = as.Date(substr(fecha, 1, 10)))
    
    for (pc in criticos_en_periodo) {
      inicio_pc <- max(as.Date(pc$inicio), periodo_inicio)
      fin_pc    <- min(as.Date(pc$fin),    periodo_fin)
      if (inicio_pc > fin_pc) next
      
      dias_criticos  <- seq(inicio_pc, fin_pc, by = "day")
      dias_con_datos <- unique(as.character(tweets_con_fecha %>%
                                              filter(fecha_solo >= inicio_pc, fecha_solo <= fin_pc) %>%
                                              pull(fecha_solo)))
      
      dias_en_blanco <- setdiff(as.character(dias_criticos), dias_con_datos)
      
      if (length(dias_en_blanco) > 0) {
        cat(sprintf("  [%s] %d días en blanco — verificando...\n",
                    pc$desc, length(dias_en_blanco)))
        
        for (fecha_blanco in dias_en_blanco) {
          url      <- url_busqueda_diaria(USUARIO, fecha_blanco, fecha_blanco)
          df_nuevo <- scrape_url_periodo(url, ids_vistos, periodo_nombre_str, verbose = FALSE)
          
          if (nrow(df_nuevo) > 0) {
            df_acumulado <- bind_rows(df_acumulado, df_nuevo) %>%
              distinct(id, .keep_all = TRUE)
            ids_vistos <- as.character(df_acumulado$id)
            write_csv(df_acumulado, periodo_archivo)
            cat(sprintf("    %s: +%d recuperados\n", fecha_blanco, nrow(df_nuevo)))
          }
          
          Sys.sleep(runif(1, 1, 2))
        }
      } else {
        cat(sprintf("  ✓ [%s]: sin días en blanco\n", pc$desc))
      }
    }
  }
  
  # ── Enriquecimiento final ──────────────────────────────────────────────────
  if (nrow(df_acumulado) == 0) {
    cat(sprintf("\n│ RESUMEN: %s — 0 tweets capturados\n", periodo_etiqueta))
    return(data.frame())
  }
  
  df_enriquecido <- df_acumulado %>%
    filter(!is.na(id), id != "") %>%
    mutate(id = as.character(id)) %>%
    distinct(id, .keep_all = TRUE) %>%
    filter(!es_retweet) %>%
    mutate(
      fecha_dt    = ymd_hms(fecha, quiet = TRUE),
      fecha_local = with_tz(fecha_dt, "America/Mexico_City"),
      texto       = str_squish(texto),
      es_retweet   = str_starts(texto, fixed("RT @")),
      es_respuesta = !es_retweet & str_starts(texto, fixed("@")),
      tipo_tweet   = case_when(
        es_retweet   ~ "retweet",
        es_respuesta ~ "respuesta",
        TRUE         ~ "original"
      ),
      periodo = periodo_nombre_str
    ) %>%
    filter(!es_retweet) %>%
    arrange(fecha_dt)
  
  write_csv(df_enriquecido, periodo_archivo)
  
  cat(sprintf("\n┌─────────────────────────────────────────────┐\n"))
  cat(sprintf("│ RESUMEN: %s\n", periodo_etiqueta))
  cat(sprintf("│ Tweets         : %s\n", format(nrow(df_enriquecido), big.mark = ",")))
  cat(sprintf("│   Originales   : %s\n", format(sum(df_enriquecido$tipo_tweet == "original"), big.mark = ",")))
  cat(sprintf("│   Respuestas   : %s\n", format(sum(df_enriquecido$es_respuesta), big.mark = ",")))
  cat(sprintf("│ Archivo        : %s\n", periodo_archivo))
  cat(sprintf("└─────────────────────────────────────────────┘\n"))
  
  return(df_enriquecido)
}

# =============================================================================
# EJECUCIÓN PRINCIPAL
# =============================================================================

cat("\n╔══════════════════════════════════════════════════════════╗\n")
cat("║   CORPUS @lopezobrador_ — INICIO DE RECOLECCIÓN v3.0   ║\n")
cat("╚══════════════════════════════════════════════════════════╝\n")
cat(sprintf("Fecha   : %s\n", format(Sys.time(), "%Y-%m-%d %H:%M:%S")))
cat(sprintf("Usuario : @%s\n\n", USUARIO))

cat("Iniciando sesión en X (se abrirá Chrome una sola vez)...\n")
py$iniciar_sesion(AUTH_TOKEN, CONFIG)
cat("✓ Navegador listo. Comenzando scraping...\n")

df_P0 <- procesar_periodo(PERIODOS$P0)
df_P1 <- procesar_periodo(PERIODOS$P1)
df_P2 <- procesar_periodo(PERIODOS$P2)

py$cerrar_sesion()

cat("\n=== Consolidación final ===\n")

df_consolidado <- bind_rows(df_P0, df_P1, df_P2) %>%
  mutate(id = as.character(id)) %>%
  distinct(id, .keep_all = TRUE) %>%
  filter(!es_retweet) %>%
  arrange(fecha_dt)

write_csv(df_consolidado, ARCHIVO_CONSOLIDADO)

cat("\n╔══════════════════════════════════════════════════════════╗\n")
cat("║              RECOLECCIÓN COMPLETADA                     ║\n")
cat("╚══════════════════════════════════════════════════════════╝\n")
cat(sprintf("Total tweets      : %s\n", format(nrow(df_consolidado), big.mark = ",")))
cat(sprintf("  P0 Pre-pres.    : %s\n", format(nrow(df_P0), big.mark = ",")))
cat(sprintf("  P1 Presidencia  : %s\n", format(nrow(df_P1), big.mark = ",")))
cat(sprintf("  P2 Post-pres.   : %s\n", format(nrow(df_P2), big.mark = ",")))
cat(sprintf("Archivo final     : %s\n", ARCHIVO_CONSOLIDADO))
cat(sprintf("Finalizado        : %s\n", format(Sys.time(), "%Y-%m-%d %H:%M:%S")))